home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / listings / v_02_07 / 2n07013a < prev    next >
Text File  |  1991-06-02  |  10KB  |  350 lines

  1. unit Timers;
  2.  
  3. interface
  4.  
  5. uses WinTypes,WObjects;
  6.  
  7. type
  8.     PTimer      = ^TTimer;
  9.  
  10.     TTimer      = object
  11.         LastEvent       : Longint;
  12.         ThisEvent       : Longint;
  13.         EventId         : integer;
  14.  
  15.         constructor InitEvent(EventId_:integer);
  16.         constructor Init;
  17.         destructor  Done; virtual;
  18.         function        Start(NewInterval:Longint):boolean;
  19.         procedure       Stop;
  20.         procedure       Fire; virtual;
  21.         function        GetInterval:Longint;
  22.     {static}
  23.         function        OutOfTimers:boolean; virtual;
  24.     private
  25.         Next            : PTimer;
  26.         Interval        : Longint;
  27.         function        SetInterval(NewInterval:Longint):boolean;
  28.         function        ReviseInterval:boolean;
  29.         function        SetBaseInterval(Interval_:Longint):boolean;
  30.         end;
  31.  
  32.     PWindowTimer        = ^TWindowTimer;
  33.     TWindowTimer        = object(TTimer)
  34.         WindowHandle: PWindowsObject;
  35.         constructor Init(WindowHandle_:PWindowsObject);
  36.         constructor InitEvent(WindowHandle_
  37.                                 :PWindowsObject;
  38.                                 EventId_:integer);
  39.                 procedure       Fire; virtual;
  40.         end;
  41.  
  42. function TimerGetInterval:Longint;
  43.  
  44.  
  45. {----------------------------------------------}
  46.  
  47. implementation
  48.  
  49. uses    WinProcs;
  50.  
  51. var
  52.         ActiveCount     : integer;
  53.     TailPtr     : PTimer;
  54.     TimerId             : integer;
  55.     IntervalGcd : Longint;
  56.     CallBack    : TFarProc;
  57.  
  58. const
  59.         TIMER_MAX_RESOLUTION
  60.         = 65535;
  61.     TIMER_MIN_RESOLUTION
  62.         = 55;
  63.  
  64. { Gcd - Greatest Common Divisor }
  65. function Gcd(a,b:Longint):Longint;
  66. var
  67.     Remainder   : Longint;
  68. begin
  69.     Remainder   := a;
  70.     if (a = 0) or (b = 0) then
  71.         Gcd     := 0
  72.     else
  73.         begin
  74.             Remainder  := b mod a;
  75.             while Remainder <> 0 do
  76.                 begin
  77.                 b               := a;
  78.                 a               := Remainder;
  79.                 Remainder       := b mod a;
  80.                 end;
  81.             Gcd := a;
  82.         end;
  83. end;
  84.  
  85. function TTimer.SetBaseInterval(Interval_:Longint):boolean;
  86. var
  87.     NewInterval : Longint;
  88.     TempReal    : real;
  89.     Finished    : boolean;
  90. begin
  91.         SetBaseInterval := TRUE;        { Assume success }
  92.     if IntervalGcd <> 0 then
  93.         NewInterval     := Gcd(IntervalGcd, Interval_)
  94.     else
  95.         NewInterval     := Interval_;
  96.     if  NewInterval < TIMER_MIN_RESOLUTION then
  97.         NewInterval     := TIMER_MIN_RESOLUTION;
  98.     if  NewInterval > TIMER_MAX_RESOLUTION then
  99.     { Use heuristic to get "nice" interval }
  100.         begin
  101.         while NewInterval > TIMER_MAX_RESOLUTION do
  102.                 NewInterval     := NewInterval div 2;
  103.         NewInterval     := (NewInterval div 1000) * 1000;
  104.         end;
  105.     if NewInterval <> IntervalGcd then
  106.         begin
  107.         if TimerId <> 0 then
  108.             KillTimer(0, TimerId);
  109.             TimerId     := SetTimer(0, 0, NewInterval,
  110.                                                                 CallBack);
  111.         repeat
  112.                 Finished        := TRUE;
  113.                 if (TimerId = 0) then
  114.                 if OutOfTimers = TRUE then
  115.                         begin
  116.                         TimerId := SetTimer(0, 0,
  117.                                 NewInterval,CallBack);
  118.                     Finished    := FALSE;
  119.                     end;
  120.         until Finished;
  121.         if TimerId = 0 then
  122.                 SetBaseInterval := FALSE
  123.         else
  124.                 IntervalGcd             := NewInterval;
  125.         end;
  126. end;
  127.  
  128. function TTimer.SetInterval(NewInterval:Longint):boolean;
  129. var
  130.         TimerWasOn, TimerIsOn, Result
  131.         : boolean;
  132. begin
  133.         Result          := TRUE;        { Assume success }
  134.     TimerWasOn  := Interval <> 0;
  135.     TimerIsOn   := NewInterval <> 0;
  136.  
  137.     { If deactivating timer }
  138.     if TimerWasOn and not(TimerIsOn) then
  139.         begin
  140.         ActiveCount     := ActiveCount - 1;
  141.         if ActiveCount = 0 then
  142.                 begin
  143.             KillTimer(0, TimerId);
  144.             TimerId             := 0;
  145.             IntervalGcd := 0;
  146.             end
  147.         else
  148.                 ReviseInterval;
  149.         end
  150.     { else if starting a timer }
  151.     else if TimerIsOn and not(TimerWasOn) then
  152.         begin
  153.         Interval        := NewInterval;
  154.         if NewInterval > TIMER_MAX_RESOLUTION then
  155.                 Result  := ReviseInterval
  156.         else
  157.                 Result  := SetBaseInterval(NewInterval);
  158.         if Result = TRUE then
  159.                 ActiveCount     := ActiveCount + 1;
  160.         end
  161.         { else if changing timer interval }
  162.     else if TimerIsOn and (NewInterval <> Interval) then
  163.         begin
  164.                 Interval        := NewInterval;
  165.         Result          := ReviseInterval;
  166.         end;
  167.         if Result = TRUE then
  168.         begin
  169.         Interval        := NewInterval;
  170.             if NewInterval > TIMER_MAX_RESOLUTION then
  171.                 Result  := ReviseInterval;
  172.         end;
  173.     SetInterval := Result;
  174. end;
  175.  
  176. function TTimer.Start(NewInterval:Longint):boolean;
  177. begin
  178.         if SetInterval(NewInterval) <> FALSE then
  179.         begin
  180.         ThisEvent       := GetTickCount;
  181.         LastEvent       := ThisEvent;
  182.         Start           := TRUE;
  183.         end
  184.     else
  185.         Start           := FALSE;
  186. end;
  187.  
  188. procedure TTimer.Stop;
  189. begin
  190.         SetInterval(0);
  191. end;
  192.  
  193. procedure       TTimer.Fire;
  194. begin
  195.         Abstract;  { User must redefine Fire function }
  196. end;
  197.  
  198. function TTimer.OutOfTimers:boolean;
  199. begin
  200.         OutOfTimers     := FALSE;  { Give up }
  201. end;
  202.  
  203. constructor TTimer.Init;
  204. begin
  205.         TTimer.InitEvent(0);
  206. end;
  207.  
  208. constructor TTimer.InitEvent(EventId_:integer);
  209. begin
  210.     if TailPtr = NIL then
  211.         begin
  212.         TailPtr                 := @Self;
  213.         Next                    := @Self;
  214.         end
  215.     else
  216.         begin
  217.         Next                    := TailPtr^.Next;
  218.         TailPtr^.Next   := @Self;
  219.         TailPtr                 := @Self;
  220.         end;
  221.     LastEvent   := 0;
  222.     ThisEvent   := 0;
  223.     Interval    := 0;
  224.     EventId             := EventId_;
  225. end;
  226.  
  227. function        TTimer.GetInterval:Longint;
  228. begin
  229.         GetInterval     := Interval;
  230. end;
  231.  
  232. function        TTimer.ReviseInterval:boolean;
  233. {
  234. ReviseInterval recalculates the greatest common
  235. divisor (gcd) of all the timers in the linked
  236. list.  If the gcd has changed, ReviseInterval
  237. resets the Windows timer by calling SetInterval.
  238. }
  239. var
  240.     Rover
  241.                 : PTimer;
  242.     NewInterval
  243.         : Longint;
  244. begin
  245.     Rover               := TailPtr;
  246.     NewInterval := 0;
  247.     repeat
  248.         Rover   := Rover^.Next;
  249.         if Rover^.Interval <> 0 then
  250.                 if NewInterval = 0 then
  251.                         NewInterval     := Rover^.Interval
  252.                 else
  253.                         NewInterval     := Gcd(NewInterval, Rover^.Interval);
  254.     until Rover = TailPtr;
  255.  
  256.     IntervalGcd         := 0;
  257.     ReviseInterval      := SetBaseInterval(NewInterval);
  258. end;
  259.  
  260.  
  261. destructor  TTimer.Done;
  262. var
  263.     Rover, Previous
  264.                 : PTimer;
  265. begin
  266.     Rover               := TailPtr;
  267.     Previous    := NIL;
  268.     repeat
  269.         if Rover^.Next = @Self then
  270.                 Previous        := Rover;
  271.         Rover   := Rover^.Next;
  272.     until Previous <> NIL;
  273.  
  274.     if Previous^.Next = Previous then
  275.         TailPtr         := NIL
  276.     else
  277.         begin
  278.         if Previous^.Next = TailPtr then
  279.                 TailPtr := Previous;
  280.             Previous^.Next      := Previous^.Next^.Next;
  281.         end;
  282.         SetInterval(0); { in case timer was active }
  283. end;
  284.  
  285. constructor TWindowTimer.Init(WindowHandle_:PWindowsObject);
  286. begin
  287.         TWindowTimer.InitEvent(WindowHandle_, 0);
  288. end;
  289.  
  290. constructor TWindowTimer.InitEvent(WindowHandle_:PWindowsObject;
  291.                                         EventId_:integer);
  292. begin
  293.         TTimer.InitEvent(EventId_);
  294.         WindowHandle    := WindowHandle_;
  295. end;
  296.  
  297. proce